#include "GameObject.h"
#include <algorithm>
#include <iostream>
#include <glm/gtc/matrix_transform.hpp>
#include "Component.h"
#include <glm/gtc/quaternion.hpp>

GameObject::GameObject(Context* context) :Object(context), transform_(1.0f)
{
    name_ = "GameObject";
}

GameObject::~GameObject()
{
  
}



void GameObject::SetPosition(const glm::vec3& position)
{
    if (position != position_)
    {
        position_ = position;
        MarkDirty();
    }
}

const glm::vec3& GameObject::GetWorldPosition()
{
    GetTransform();
    return worldPosition_;
}

   
void GameObject::SetScale(const glm::vec3& scale)
{
    if (scale != scale_)
    {
        scale_ = scale;
        MarkDirty();
    }
}

void GameObject::SetScale(float scale)
{
    SetScale(glm::vec3(scale, scale, scale));
}

void GameObject::SetRotation(const glm::vec3& angle)
{
    if (angle != rotation_)
    {
        rotation_ = angle;
        MarkDirty();
    }
}

void GameObject::SetRotation(const glm::quat& q)
{
	glm::vec3 euler = glm::degrees(glm::eulerAngles(q));
    SetRotation(euler);
}

const glm::mat4x4& GameObject::GetTransform() const
{
    if (positionDirty_)
    {
        glm::mat4x4 parentTransform(1.0f);

        if (parent_)
        {
            parentTransform = parent_->GetTransform();
        }

        glm::mat4x4 mainTransform(1.0);
        mainTransform = glm::translate(mainTransform, position_);
        glm::quat q = glm::quat(glm::radians(rotation_));
        glm::mat4 RotationMatrix = glm::mat4_cast(q);
        mainTransform *= RotationMatrix;
        mainTransform = glm::scale(mainTransform, scale_);
           
        transform_ = parentTransform * mainTransform;
        positionDirty_ = false;
            
        glm::vec4 topLeftCorner = transform_ * glm::vec4(0.f, 0.f, 0.f, 1.f);
        worldPosition_ = glm::vec3(topLeftCorner.x, topLeftCorner.y, topLeftCorner.z);
    }

    return transform_;
}


void GameObject::MarkDirty()
{
    positionDirty_ = true;

    for (auto i = components_.begin(); i != components_.end(); ++i)
        (*i)->MarkDirty();

    for (auto i = children_.begin(); i != children_.end(); ++i)
        (*i)->MarkDirty();    
}

void GameObject::AddChild(std::shared_ptr<GameObject> element)
{
	if (!element || element.get() == this || element->parent_ == this || parent_ == element.get())
		return;

	children_.push_back(element);

	if (element->parent_)
	{
		element->parent_->RemoveChild(element);
	}

	element->parent_ = this;
	element->MarkDirty();
}

void GameObject::RemoveChild(std::shared_ptr<GameObject> element)
{
	for (size_t i = 0; i < children_.size(); i++)
	{
		if (children_[i] == element)
		{
			children_.erase(children_.begin() + i);
			return;
		}
	}
}

void GameObject::SetName(std::string name)
{
    if (name.length() < 24)
    {
        name_ = name;
    }
}
void GameObject::AddComponent(std::shared_ptr<Component> comp)
{
    if(!comp) {
        return;
    }
    if (comp->GetOwner() == this)
    {
        return;
    }

    if (comp->GetOwner()) {
        comp->GetOwner()->RemoveComponent(comp);
    }

    components_.push_back(comp);
    comp->OnInit(this);
    comp->OnStart();
    comp->OnEnable();
}
void GameObject::RemoveComponent(std::shared_ptr<Component> target)
{
    for (int i = 0; i < components_.size(); i++) {
        if (components_[i] == target) {
            components_.erase(components_.begin() + i);
            target->OnDisable();
            target->OnDestory();
        }
    }
}
GameObject* GameObject::Clone()
{
    GameObject* rootObject = new GameObject(context_);
    auto& coms = GetComponents();
    for (size_t i = 0; i < coms.size(); i++)
    {
        auto c = coms[i]->Clone();
        if (c)
        {
            rootObject->AddComponent(c);
        }
    }
    rootObject->SetName(name_);
    rootObject->SetPosition(position_);
    rootObject->SetRotation(rotation_);
    rootObject->SetScale(scale_);
    return rootObject;
}


